package analizator;

import analizator.CuvantCheie.Keyword;
import java.util.ArrayList;

/**
 *
 * @author bufu
 */
public class Analizator {

    private FileProvider m_provider;
    //private boolean m_ignoreComments;
    private ArrayList<Atom> m_list;
    private boolean debug = false;

    public Analizator(FileProvider provider) {//, boolean ignoreComments) {
        m_list = new ArrayList<>();
        m_provider = provider;
        //m_ignoreComments = ignoreComments;
    }

    public void putUnusedAtom(Atom a) {
        System.out.println("putting unused atom " + a);
        m_list.add(a);
    }

    public void putUnusedAtoms(ArrayList<Atom> a) {
        if (a == null) {
            return;
        }
        while (a.size() > 0) {
            putUnusedAtom(a.remove(0));
        }
    }

    public Atom debugGetNextAtom() {
        Atom a = getNextAtom();
        if (a != null) {
            //System.out.println(a);
        }
        return a;
    }

    private Atom getNextAtom() {
        if (m_list.size() > 0) {
            return m_list.remove(m_list.size() - 1);
        }
        while (m_provider.nextSpace()) {
            m_provider.getCharacter();
        }
        Caracter c = m_provider.getCharacter();
        if (c == null) {
            return null;
        }
        if (c.value == '"') {
            return stareSir1("", c.line, c.column);
        } else if (c.value == '\'') {
            return stareConstChar3();
        } else if (c.value == '{') {
            System.out.println(stareComment5());
            return getNextAtom();
        } else if (c.isSpace()) {
            System.out.println(stareSpace7());
            return getNextAtom();
        } else if (c.value == ';' || c.value == ',') {
            return stareDelimitator9(c);
        } else if (c.value == ':') {
            return stare11(c);
        } else if (c.value == '.') {
            return stare13(c);
        } else if (c.value == '+' || c.value == '-' || c.value == '*' || c.value == '/' || c.value == '(' || c.value == ')' || c.value == '[' || c.value == ']' || c.value == '=') {
            return stareOperator14(c);
        } else if (c.value == '<') {
            return stareOperator15(c);
        } else if (c.value == '>') {
            return stareOperator17(c);
        } else if (c.isLetter()) {
            return stare19(c);
        } else if (c.isCifra()) {
            return stareNumar26(c);
        } else {
            System.out.println("Eroare in analiza sintactica");
            return null;
        }

        //Atom a;
        /*
         * a = Delimitator.makeAtom(m_provider); if (a != null) { return a; }
         */
        /*
         * a = Operator.makeAtom(m_provider); if (a != null) { return a; }
         */
        /*
         * a = CuvantCheie.makeAtom(m_provider); if (a != null) { return a; } a
         * = Identificator.makeAtom(m_provider); if (a != null) { return a; }
         */
        /*
         * a = Comment.makeAtom(m_provider); if (a != null) { return
         * m_ignoreComments ? debugGetNextAtom() : a; }
         */
        /*
         * a = Sir.makeAtom(m_provider); if (a != null) { return a; }
         */
        /*
         * a = getNumar(); if (a != null) { return a; }
         */
    }

    private Atom stareNumar40(double value, int semn, int ex, int line, int column) {
        if (debug) {
            System.out.println("40 cu " + value);
        }
        while (ex > 0) {
            if (semn == 1) {
                value *= 10;
            } else {
                value /= 10;
            }
            ex--;
        }
        return stareNumar34(value, line, column);
    }

    private Atom stareNumar39(double value, int semn, int ex, int line, int column) {
        if (debug) {
            System.out.println("39 cu " + value);
        }
        return stareNumar40(value, semn, ex, line, column);
    }

    private Atom stareNumar38(double value, int semn, int ex, int line, int column) {
        if (debug) {
            System.out.println("38 cu " + value);
        }
        Caracter a = m_provider.getCharacter();
        if (a.isCifra()) {
            return stareNumar39(value, semn, ex * 10 + a.intValue(), line, column);
        } else {
            m_provider.putCharacter(a);
            return stareNumar40(value, semn, ex, line, column);
        }
    }

    private Atom stareNumar37(double value, int semn, int line, int column) {
        if (debug) {
            System.out.println("37 cu " + value);
        }
        Caracter a = m_provider.getCharacter();
        if (a.isCifra()) {
            return stareNumar38(value, semn, a.intValue(), line, column);
        } else {
            m_provider.putCharacter(a);
            return null;
        }
    }

    private Atom stareNumar36(double value, char a, int line, int column) {
        if (debug) {
            System.out.println("36 cu " + value);
        }
        if (a == '+') {
            return stareNumar37(value, 1, line, column);
        } else {
            return stareNumar37(value, -1, line, column);
        }
    }

    private Atom stareNumar35(double value, int line, int column) {
        if (debug) {
            System.out.println("35 cu " + value);
        }
        Caracter a = m_provider.getCharacter();
        if (a.value == '+' || a.value == '-') {
            return stareNumar36(value, a.value, line, column);
        } else {
            m_provider.putCharacter(a);
            return stareNumar37(value, 1, line, column);
        }
    }

    private Atom stareNumar34(double value, int line, int column) {
        if (debug) {
            System.out.println("34 cu " + value);
        }
        return new NumarReal(value, line, column);
    }

    private Atom stareNumar33(double value, double p, int line, int column) {
        if (debug) {
            System.out.println("33 cu " + value);
        }
        Caracter a = m_provider.getCharacter();
        if (a.isCifra()) {
            return stareNumar33(value * 10 + a.intValue(), p * 10, line, column);
        } else if (a.value == 'E' || a.value == 'e') {
            return stareNumar35(value / p, line, column);
        } else {
            m_provider.putCharacter(a);
            return stareNumar34(value / p, line, column);
        }
    }

    private Atom stareNumar32(int value, int line, int column, Caracter punct) {
        if (debug) {
            System.out.println("32 cu " + value);
        }
        Caracter a = m_provider.getCharacter();
        if (a.isCifra()) {
            return stareNumar33(value * 10 + a.intValue(), 10, line, column);
        } else if (a.value == '.') {
            m_provider.putCharacter(a);
            m_provider.putCharacter(punct);
            return stareNumar27(value, line, column);
        } else {
            m_provider.putCharacter(a);
            return stareNumar27(value, line, column);
        }
    }

    private Atom stareNumar31(int value, int line, int column) {
        if (debug) {
            System.out.println("31 cu " + value);
        }
        Caracter a = m_provider.getCharacter();
        if (a.isCifra()) {
            return stareNumar31(value * 10 + a.intValue(), line, column);
        } else if (a.value == '.') {
            return stareNumar32(value, line, column, a);
        } else {
            m_provider.putCharacter(a);
            return stareNumar27(value, line, column);
        }
    }

    private Atom stareNumar30(int baza, int value, int line, int column) {
        if (debug) {
            System.out.println("30 cu " + value + " in baza " + baza);
        }
        Caracter a = m_provider.getCharacter();
        if (a.validForBase(baza)) {
            return stareNumar30(baza, value * baza + a.intValue(), line, column);
        } else {
            m_provider.putCharacter(a);
            return stareNumar27(value, line, column);
        }
    }

    private Atom stareNumar29(int value, int line, int column) {
        if (debug) {
            System.out.println("29 cu " + value);
        }
        return stareNumar30(value, 0, line, column);
    }

    private Atom stareNumar28(int value, int line, int column) {
        if (debug) {
            System.out.println("28 cu " + value);
        }
        Caracter a = m_provider.getCharacter();
        if (a.value == '@') {
            return stareNumar29(value, line, column);
        } else if (a.isCifra()) {
            return stareNumar31(value * 10 + a.intValue(), line, column);
        } else if (a.value == '.') {
            return stareNumar32(value, line, column, a);
        } else {
            m_provider.putCharacter(a);
            return stareNumar27(value, line, column);
        }
    }

    private Atom stareNumar27(int value, int line, int column) {
        if (debug) {
            System.out.println("27 cu " + value);
        }
        return new NumarIntreg(value, line, column);
    }

    private Atom stareNumar26(Caracter c) {
        if (debug) {
            System.out.println("26 cu " + c);
        }
        Caracter a = m_provider.getCharacter();
        if (a.isCifra()) {
            return stareNumar28(c.intValue() * 10 + a.intValue(), c.line, c.column);
        } else if (a.value == '@') {
            return stareNumar29(c.intValue(), c.line, c.column);
        } else if (a.value == '.') {
            return stareNumar32(c.intValue(), c.line, c.column, a);
        } else {
            m_provider.putCharacter(a);
            return stareNumar27(c.intValue(), c.line, c.column);
        }
    }

    private CuvantCheie stare25(Keyword key, int line, int column) {
        return new CuvantCheie(key, line, column);
    }

    private Atom stare24(String value, int line, int column) {
        String cmp = value.toUpperCase();
        for (Keyword key : Keyword.values()) {
            if (cmp.equals(key.toString())) {
                return stare25(key, line, column);
            }
        }
        return new Identificator(value, line, column);
    }

    private Atom stare23(String value, int line, int column) {
        return new Identificator(value, line, column);
    }

    private Atom stare22(String value, int line, int column) {
        Caracter a = m_provider.getCharacter();
        if (a.isCifra() || a.isLetter()) {
            return stare22(value + a.value, line, column);
        } else {
            m_provider.putCharacter(a);
            return stare23(value, line, column);
        }
    }

    private Atom stare21(String value, int line, int column) {
        Caracter a = m_provider.getCharacter();
        if (a.isCifra()) {
            return stare22(value + a.value, line, column);
        } else if (a.isLetter()) {
            return stare21(value + a.value, line, column);
        } else {
            m_provider.putCharacter(a);
            return stare24(value, line, column);
        }
    }

    private Atom stare20(Caracter c) {
        return c;
    }

    private Atom stare19(Caracter c) {
        Caracter a = m_provider.getCharacter();
        if (a.isLetter()) {
            return stare21(c.value + "" + a.value, c.line, c.column);
        } else if (a.isCifra()) {
            return stare22(c.value + "" + a.value, c.line, c.column);
        } else {
            m_provider.putCharacter(a);
            return stare20(c);
        }
    }

    private Operator stareOperator18(Caracter c) {
        return new Operator(">", c.line, c.column);
    }

    private Operator stareOperator17(Caracter c) {
        Caracter a = m_provider.getCharacter();
        if (a.value == '=') {
            return stare12(">=", c.line, c.column);
        } else {
            m_provider.putCharacter(a);
            return stareOperator18(c);
        }
    }

    private Operator stareOperator16(Caracter c) {
        return new Operator("<", c.line, c.column);
    }

    private Operator stareOperator15(Caracter c) {
        Caracter a = m_provider.getCharacter();
        if (a.value == '>' || a.value == '=') {
            return stare12(c.value + "" + a.value, c.line, c.column);
        } else {
            m_provider.putCharacter(a);
            return stareOperator16(c);
        }
    }

    private Atom stareOperator14(Caracter c) {
        return stare12(c.value + "", c.line, c.column);
    }

    private Atom stare13(Caracter c) {
        Caracter a = m_provider.getCharacter();
        if (a.value == '.') {
            return stareDelimitator10("..", c.line, c.column);
        } else {
            m_provider.putCharacter(a);
            return stareDelimitator10(".", c.line, c.column);
        }
    }

    private Operator stare12(String value, int line, int column) {
        return new Operator(value, line, column);
    }

    private Atom stare11(Caracter c) {
        Caracter a = m_provider.getCharacter();
        if (a.value == '=') {
            return stare12(":=", c.line, c.column);
        } else {
            m_provider.putCharacter(a);
            return stareDelimitator10(":", c.line, c.column);
        }
    }

    private Delimitator stareDelimitator10(String value, int line, int column) {
        return new Delimitator(value, line, column);
    }

    private Delimitator stareDelimitator9(Caracter c) {
        return stareDelimitator10("" + c.value, c.line, c.column);
    }

    private Sir stareSir1(String value, int linie, int coloana) {
        Caracter c = m_provider.getCharacter();
        if (c.value == '"') {
            return stareSir2(value, linie, coloana);
        } else {
            return stareSir1(value + c.value, linie, coloana);
        }
    }

    private Sir stareSir2(String value, int linie, int coloana) {
        return new Sir(value, linie, coloana);
    }

    private ConstChar stareConstChar3() {
        Caracter c = m_provider.getCharacter();
        Caracter a = m_provider.getCharacter();
        if (a.value == '\'') {
            return stareConstChar4(c);
        } else {
            return null;
        }
    }

    private ConstChar stareConstChar4(Caracter c) {
        return new ConstChar(c);
    }

    private Comment stareComment5() {
        Caracter c = m_provider.getCharacter();
        if (c.value == '}') {
            return stareComment6();
        } else {
            return stareComment5();
        }
    }

    private Comment stareComment6() {
        return new Comment();
    }

    private WhiteSpace stareSpace7() {
        Caracter c = m_provider.getCharacter();
        if (c.isSpace()) {
            return stareSpace7();
        } else {
            return stareSpace8();
        }
    }

    private WhiteSpace stareSpace8() {
        return new WhiteSpace();
    }

    private NumarIntreg getNumar() {
        NumarIntreg n = getNrReal();
        if (n == null) {
            n = getNrIntreg();
        }
        return n;
    }

    private NumarIntreg getNrIntreg() {
        NumarIntreg n = getNrBazaAtCifra();
        if (n == null) {
            n = getNrBaza10();
        }
        return n;
    }

    private NumarIntreg getNrBazaAtCifra() {
        Caracter head = m_provider.getCharacter(), temp = head;
        if (head == null) {
            return null;
        }
        if (!head.isCifra()) {
            m_provider.putCharacter(head);
            return null;
        }
        ArrayList<Caracter> list = new ArrayList<>();
        list.add(temp);
        int baza = 0;
        while (temp.isCifra()) {
            baza = baza * 10 + temp.intValue();
            temp = m_provider.getCharacter();
            list.add(temp);
            //System.out.println(value);
        }
        if (temp.value != '@' || baza > 16) {
            m_provider.putCharacters(list);
            return null;
        }
        int valoare = 0;
        temp = m_provider.getCharacter();
        list.add(temp);
        if (temp == null || !temp.validForBase(baza)) {
            m_provider.putCharacters(list);
            return null;
        }
        while (temp != null && temp.validForBase(baza)) {
            valoare = valoare * baza + temp.intValue();
            temp = m_provider.getCharacter();
            list.add(temp);
        }
        list.remove(list.size() - 1);
        if (!m_provider.nextNumberEnd(temp)) {
            m_provider.putCharacters(list);
            return null;
        }
        System.out.println("test" + baza);
        return new NumarIntreg(valoare, head.line, head.column);
    }

    private NumarIntreg getNrBaza10() {
        Caracter head = m_provider.getCharacter(), temp = head;
        if (head == null) {
            return null;
        }
        if (!head.isCifra()) {
            m_provider.putCharacter(head);
            return null;
        }
        ArrayList<Caracter> list = new ArrayList<>();
        list.add(temp);
        int value = 0;
        while (temp.isCifra()) {
            value = value * 10 + temp.value - '0';
            temp = m_provider.getCharacter();
            list.add(temp);
            //System.out.println(value);
        }
        m_provider.putCharacter(temp);
        /*
         * list.remove(list.size() - 1); if (!m_provider.nextNumberEnd(temp) &&
         * temp.value != '.' && !(allowE && (temp.value == 'e' || temp.value ==
         * 'E'))) { m_provider.putCharacters(list); return null; }
         */
        return new NumarIntreg(value, head.line, head.column);
    }

    private NumarIntreg getNrReal() {
        NumarIntreg a = getNrBaza10();
        if (a == null) {
            return null;
        }
        Caracter c = m_provider.getCharacter();
        if (c.value != '.') {
            return a;
        }
        Caracter temp = m_provider.getCharacter();
        if (temp.value == '.' || !temp.isCifra()) {
            m_provider.putCharacter(temp);
            m_provider.putCharacter(c);
            return a;
        } else {
            m_provider.putCharacter(temp);
        }
        NumarIntreg b = getNrBaza10();
        if (b == null) {
            return null;
        }
        //System.out.println(a.m_value);
        double rest = b.m_value;
        while (rest > 1) {
            rest /= 10;
        }
        a.m_value += rest;
        temp = m_provider.getCharacter();
        if (temp.value != 'e' && temp.value != 'E') {
            m_provider.putCharacter(temp);
            return a;
        }
        temp = m_provider.getCharacter();
        int semn = 1, exponent;
        if (temp.value == '-') {
            semn = -1;
            temp = m_provider.getCharacter();
        } else if (temp.value == '+') {
            temp = m_provider.getCharacter();
        }
        if (!temp.isCifra()) {
            return null;
        }
        exponent = temp.intValue();
        temp = m_provider.getCharacter();
        if (temp.isCifra()) {
            exponent = exponent * 10 + temp.intValue();
        } else {
            m_provider.putCharacter(temp);
        }
        if (exponent == 0) {
            a.m_value = 0;
        } else {
            while (exponent > 0) {
                if (semn == 1) {
                    a.m_value *= 10;
                } else {
                    a.m_value /= 10;
                }
                exponent--;
            }
        }
        return a;
    }

    public boolean readKeyword(Keyword key) {
        Atom a = debugGetNextAtom();
        boolean result;
        if (a instanceof CuvantCheie) {
            result = ((CuvantCheie) a).m_keyword == key;
        } else {
            result = false;
        }
        if (!result) {
            putUnusedAtom(a);
        }
        return result;
    }

    public Identificator readIdentificator() {
        Atom a = debugGetNextAtom();
        if (a instanceof Identificator) {
            return (Identificator) a;
        } else {
            putUnusedAtom(a);
            return null;
        }
    }

    public boolean checkOperator(String match) {
        Atom a = debugGetNextAtom();
        boolean result;
        if (a instanceof Operator) {
            result = ((Operator) a).m_value.equals(match);
        } else {
            result = false;
        }
        if (!result) {
            putUnusedAtom(a);
        }
        return result;
    }

    public boolean checkDelimitator(String match) {
        Atom a = debugGetNextAtom();
        boolean result;
        if (a instanceof Delimitator) {
            result = ((Delimitator) a).m_value.equals(match);
        } else {
            result = false;
        }
        if (!result) {
            putUnusedAtom(a);
        }
        return result;
    }

    public static void main(String args[]) {
        FileProvider provider = new FileProvider("exemplu.txt");
        Analizator alex = new Analizator(provider);
        Atom a;
        while ((a = alex.getNextAtom()) != null) {
            System.out.println(a);
        }
    }

    public interface Atom {
    };

    public class WhiteSpace implements Atom {

        @Override
        public String toString() {
            return "Whitespace";
        }
    }
}
